package cn.mystylefree.sqlSession;

import cn.mystylefree.config.BoundSql;
import cn.mystylefree.domain.User;
import cn.mystylefree.po.Configuration;
import cn.mystylefree.po.MappedStatement;
import cn.mystylefree.utils.GenericTokenParser;
import cn.mystylefree.utils.ParameterMapping;
import cn.mystylefree.utils.ParameterMappingTokenHandler;
import lombok.SneakyThrows;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Inherited;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

/**
 * @program my-project-model
 * @description:
 * @author: lee
 * @create: 2022/06/15 12:57
 */
public class SimpleExecutor implements Executor {
    /**
     * 编写jdbc代码
     *
     * @param configuration
     * @param mappedStatement
     * @param params
     * @param <E>
     * @return
     */
    @SneakyThrows
    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) {
        //step1 注册驱动，获取连接
        Connection connection = configuration.getDataSource().getConnection();
        //step2 获取sql语句  select * from user where id= #{id} and name= #{name}
        //jdbc不能识别
        // select * from user where id= ? and name= ? 转换过程中对 #{} 中对值进行解析存储
        //为啥不直接在写sql时就用？ 因为是一种需求 传入对是对象，根据占位符中对值，到对象属性中查找
        String sql = mappedStatement.getSql();

        //step3 转换sql语句
        BoundSql boundSql = getBoundSql(sql);
        //step4 获取预处理对象 preparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        //step5 设置参数，如何进行替换呢？？
        //替换中的数据是什么变量怎么进行设计的呢？？？
        //这里的数据是保存在   BoundSql中的
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        //需要这个下标
        //获取到参数的全地址
        String parameterType = mappedStatement.getParameterType();
        Class<?> parameterTypeClass = getClassType(parameterType);

        for (int i = 0; i < parameterMappingList.size(); i++) {

            ParameterMapping parameterMapping = parameterMappingList.get(i);
            //id 和 username
            String content = parameterMapping.getContent();
            //通过反射 根据 content 获取到对象中的属性值--在获取到user参数中传递的具体值

            //（1）获取实体的全路径 String resultType = node.attributeValue("resultType");
            //parameterType="cn.mystylefree.my_persistence_demo.domain.User
            Field declaredField = parameterTypeClass.getDeclaredField(content);
            //获取到属性对象之后，下面是获取属性对象的值的方法
            declaredField.setAccessible(true);//暴力访问

            //这里获取的就是属性的值 从入参获取
//            if (params.length>0){
                Object o = declaredField.get(params[0]);
//            Integer o = (Integer) declaredField.get(params[0]);
                preparedStatement.setObject(i + 1, o);//sql设置参数的下标是从1开始
//            }

        }

        //step6 执行sql
        ResultSet resultSet = preparedStatement.executeQuery();

        //step7 封装返回结果集
        //获取返回对象class
        Class<?> resultTypeClass = Class.forName(mappedStatement.getResultType());
        /*这就是封装好的对象了*/
        Object instance = resultTypeClass.getConstructor().newInstance();
        List<Object> objects = new ArrayList<>();
        while (resultSet.next()) {
            //元数据  这里包含了查询字段的名称
            ResultSetMetaData metaData = resultSet.getMetaData();
            //为啥从1开始？
            for (int i = 1; i <=metaData.getColumnCount(); i++) {//查询结果的列数（表中的字段个数）
                //字段名
                String columnName = metaData.getColumnName(i);//这个ColumnName的下标从1开始
                //字段的值
                Object value = resultSet.getObject(columnName);
                //使用反射或者内省，根据数据库表和实体的对应关系，完成封装
                //利用有参数构造，在创建后 就会从 resultType中获取 columnName （这个属性）生成读写方法
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                //写方法
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(instance, value);//这里就把value值封装到了instance对象中了
            }
            objects.add(instance);
        }

        return (List<E>) objects;
    }

    private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
        if (parameterType != null) {
            Class<?> aClass = Class.forName(parameterType);
            return aClass;
        }
        return null;
    }

    /**
     * 完成对 #{}的解析工作：
     * <p>
     * 1.将#{}使用 ？进行代替
     * 2.解析成#{} 中的值进行存储
     *
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        //标记处理类：配置标记解析器 来完成对占位符对解析处理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        //解析出来的sql
        String parseSql = genericTokenParser.parse(sql);
        //2.解析成#{} 中的值进行存储
        //#{}中解析出来的参数名称
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        //面向对象思想，将sql和参数名封装到BoundSql中
        BoundSql boundSql = new BoundSql(parseSql, parameterMappings);

        return boundSql;
    }


    public void test ()throws Exception{
        User user=new User();
        user.setUsername("xx");
        user.setId(1);
        Class<?> aClass = User.class;
        Field id = aClass.getDeclaredField("id");
        id.setAccessible(true);
        Object o = id.get(user);
        System.out.println(o);
    }

    public static void main(String[] args) throws Exception {
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        simpleExecutor.test();
    }


}
